#include <jni.h>
#include <string>
#include <unistd.h>
#include <stdlib.h>
#include "works/work_data.h"
#include "server/server_thread_pool.h"
#include "works/work_item_queue.h"
#include "utils/app_sys_log.h"
#include "server/rocky_server.h"
#include "client/rocky_client.h"

#ifdef __cplusplus
extern "C" {
#endif

JavaVM *g_JVM = NULL;
static jclass gJavaThreadPoolTest_class;
static jmethodID gJavaThreadPoolTest_onUnsolicited_methodID;
static jobject gJavaThreadPoolTest_object;

void* my_work_handler(void* data, void* ctx) {
    //app_sys_debug("work handler data: %d", data);
    JNIEnv* env = NULL;
    if( g_JVM->AttachCurrentThread(&env,NULL) <0) {
        app_sys_error("attach to current thread failed");
        return  NULL;
    }
    env->CallVoidMethod(gJavaThreadPoolTest_object,
                            gJavaThreadPoolTest_onUnsolicited_methodID);
    g_JVM->DetachCurrentThread();
    return NULL;
}


void server_on_connection_data_received(RockyConnection* connection, void* data, size_t data_size) {
    char* str = (char *)calloc(data_size + 1, sizeof(char));
    memcpy(str, data, data_size);
    app_sys_debug("server %p: receive %d bytes data: %s", connection, data_size, (char*)str);

    app_sys_debug("server %p: send %d bytes data: %s", connection, data_size, (char*)str);
    rocky_connection_write_data(connection, data, data_size);
}

void server_on_connection_disconnected(RockyConnection* connection) {
    app_sys_warn("server: remote disconnected");
}

void* server_thread_runner(void* arg) {
    RockyServer *server = (RockyServer *)arg;

    rocky_server_loop(server);

    pthread_exit(NULL);
}

void client_on_connection_data_receive(RockyConnection* connection, void* data, size_t data_size) {
    char* str = (char *)calloc(data_size + 1, sizeof(char));
    memcpy(str, data, data_size);
    app_sys_debug("client %p: receive %d bytes data: %s", connection, data_size, (char*)str);
}

void client_on_connection_disconnected(RockyConnection* connection) {
    app_sys_warn("client: remote disconnected");
}

void* client_thread_runner(void* arg) {
    RockyClient* client = (RockyClient *)arg;

    rocky_client_loop(client);

    pthread_exit(NULL);
}

JNIEXPORT jstring JNICALL
Java_com_johnnyyin_librockysocket_ThreadPoolTest_stringFromJNI(
        JNIEnv *env,
        jobject object) {
    app_sys_debug("%s %d", __func__, __LINE__);
    env->GetJavaVM(&g_JVM);
    jclass c = env->FindClass("com/johnnyyin/librockysocket/ThreadPoolTest");
    gJavaThreadPoolTest_class = (jclass)env->NewGlobalRef(c);
    gJavaThreadPoolTest_onUnsolicited_methodID = env->GetMethodID(gJavaThreadPoolTest_class, "onUnsolicited", "()V");
    gJavaThreadPoolTest_object = env->NewGlobalRef(object);

    pthread_t server_pthread_t = 0;
    RockyServer *server = rocky_server_create(10, 1000, server_on_connection_data_received,
                                              server_on_connection_disconnected);
    if (server == NULL) {
        app_sys_error("create server failed");
        exit(-1);
    }
    pthread_create(&server_pthread_t, NULL, server_thread_runner, server);

    sleep(1);

    int client_size = 10;
    pthread_t *client_pthread_ts = (pthread_t *) malloc(client_size * sizeof(pthread_t));
    RockyClient **clients = (RockyClient **) malloc(client_size * sizeof(RockyClient *));
    for (int i = 0; i < client_size; i++) {
        clients[i] = NULL;
    }

    for (int i = 0; i < client_size; i++) {
        RockyClient *client = rocky_client_create(1000, client_on_connection_data_receive,
                                                  client_on_connection_disconnected);
        if(client == NULL) {
            app_sys_error("create client failed exit %d", i);
            exit(-1);
        }
        clients[i] = client;
        pthread_attr_t attr;
        pthread_attr_init(&attr);
        pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
        pthread_create(&client_pthread_ts[i], &attr, client_thread_runner, client);
    }
    sleep(2);

    const char *DATA_ARRAY[10] = {
            "hello",
            "mifans",
            "are you ok?",
            "this is lebus",
            "do you like me?",
            "i love you idia mifans",
            "i am mifans",
            "you are mifans",
            "we are all mifans.",
            "we are family."
    };

    for (int j = 0; j < 10; j++) {
        for (int i = 0; i < client_size; i++) {
            RockyClient *client = clients[i];
            app_sys_debug("client %p: send %d bytes data: %s", client, strlen(DATA_ARRAY[j]), DATA_ARRAY[j]);
            rocky_client_write_data(client, (void *) DATA_ARRAY[j], strlen(DATA_ARRAY[j]));
        }
    }
    sleep(1);

    if(clients != NULL) {
        for (int i = 0; i < client_size; i++) {
            RockyClient *client = clients[i];
            if(client != NULL) {
                rocky_client_destroy(client);
                clients[i] = NULL;
            }
        }
    }
    sleep(1);

    rocky_server_destroy(server);

    pthread_join(server_pthread_t, NULL);


    if(client_pthread_ts != NULL) {
        free(client_pthread_ts);
    }
    free(clients);

    app_sys_debug("all thing are perfectly done!");
    return env->NewStringUTF("perfect");
}


#ifdef __cplusplus
}
#endif